﻿// client-win32.cpp : 定义应用程序的入口点。
//

#include <SDKDDKVer.h>

#define WIN32_LEAN_AND_MEAN             // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>

// C 运行时头文件
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <shellapi.h>
#include <vector>
#include <iostream>
#include "client-win32.h"

#define WM_TRAY (WM_USER + 100)
#define WM_TASKBAR_CREATED RegisterWindowMessage(TEXT("TaskbarCreated"))

#define APP_NAME	TEXT("Blade")


#define ID_SHOW			40001
#define ID_AUTO			40002 // 开机运行
#define ID_DISAUTO		40003 // 不开机运行
#define ID_ENABLE		40004 // 启用代理
#define ID_DISABLE		40005 // 不启用代理
#define ID_PAC_ENABLE	40006 // 启用pac代理
#define ID_PAC_DISABLE	40007 // 不启用pac代理
#define ID_SWITCH_PROXY	40008 // 选择代理服务器
#define ID_EDITJSON		40009 // 编辑配置文件
#define ID_OPENCUR		40010 // 打开当前文件夹
#define ID_EXIT			40011 // 退出


typedef void(*GetString)(char**);

NOTIFYICONDATA nid;		//托盘属性
HMENU hMenu;			//托盘菜单
HMENU hProxyMenu;			//代理服务器菜单
HMODULE hd;
char* listenAddr = NULL;
char* pacListenAddr = NULL;
char* remoteAddr = NULL;
char  currentDir[256] = {};
std::vector<std::string> arr;

std::vector<std::string> split(const std::string& str, const std::string& delim)
{
	std::vector<std::string> substr_list;
	if (str.empty() || delim.empty()) {
		substr_list.push_back(str);
		return substr_list;
	}
	std::string::size_type start = 0;
	while (true) {
		std::string::size_type pos = str.find(delim, start);
		if (pos == start) {
			substr_list.push_back(std::string());
			start += delim.length();
			continue;
		}
		else if (pos == std::string::npos) {
			substr_list.push_back(str.substr(start));
			break;
		}
		else {
			substr_list.push_back(str.substr(start, pos - start));
			start = pos + delim.length();
		}
	}
	return substr_list;
}

std::wstring s2ws(const std::string& s)
{
	int len;
	int slength = (int)s.length(); 
	len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0) + 2;
	wchar_t* buf = new wchar_t[len];
	memset(buf, 0, len);
	MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
	buf[len - 2] = 0;
	std::wstring r(buf);
	delete[] buf;
	return r;
}

void CheckRegKey()
{
	HKEY hOpen;
	int nRet = 0;
	nRet = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", NULL, KEY_ALL_ACCESS, &hOpen);
	if (ERROR_SUCCESS != nRet) {
		MessageBoxA(NULL, "打开注册表失败 Run", "Error", MB_OK);
		AppendMenu(hMenu, MF_STRING, ID_DISAUTO, TEXT("开机运行"));
	}
	else 
	{
		char path[256] = {};
		GetModuleFileNameA(NULL, path, 256);
		char buf0[1024] = {};
		DWORD size = 1024;
		nRet = RegQueryValueExA(hOpen, "Blade", NULL, NULL, (LPBYTE)buf0, &size);
		if (buf0[0] != 0)
		{
			if (strcmp(buf0, path) == 0) {
				AppendMenu(hMenu, MF_STRING, ID_AUTO, TEXT("✔开机运行"));
			}
			else {
				MessageBoxA(NULL, "注册表路径与当前路径不一致:", "警告", MB_OK);
				AppendMenu(hMenu, MF_STRING, ID_DISAUTO, TEXT("开机运行"));
			}
		}
		else {
			AppendMenu(hMenu, MF_STRING, ID_DISAUTO, TEXT("开机运行"));
		}
		RegCloseKey(hOpen);
	}
	
	// AutoConfigURL 是pac的配置文件注册表信息
	nRet = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", NULL, KEY_ALL_ACCESS, &hOpen);
	if (ERROR_SUCCESS != nRet) {
		MessageBoxA(NULL, "打开注册表失败 Internet Settings", "Error", MB_OK);
		AppendMenu(hMenu, MF_STRING, ID_DISABLE, TEXT("启用"));
	}
	else 
	{
		char buf0[1024] = {};
		char buf1[1024] = {};
		char buf2[1024] = {};
		DWORD size = 1024;
		nRet = RegQueryValueExA(hOpen, "ProxyEnable", NULL, NULL, (LPBYTE)buf0, &size);
		size = 1024;
		nRet = RegQueryValueExA(hOpen, "ProxyServer", NULL, NULL, (LPBYTE)buf1, &size);
		size = 1024;
		nRet = RegQueryValueExA(hOpen, "AutoConfigURL", NULL, NULL, (LPBYTE)buf2, &size);
		if (buf0[0] == 1)
		{
			if (strcmp(buf1, listenAddr) == 0) {
				AppendMenu(hMenu, MF_STRING, ID_ENABLE, TEXT("✔启用全局代理"));
			} else {
				MessageBoxA(NULL, "默认代理地址和当前配置不一致:", "警告", MB_OK);
				AppendMenu(hMenu, MF_STRING, ID_DISABLE, TEXT("启用全局代理"));
			}
		}else {
			AppendMenu(hMenu, MF_STRING, ID_DISABLE, TEXT("启用全局代理"));
		}
		if (buf2[0] != 0){
			if (buf0[0] == 1)
			{
				MessageBoxA(NULL, "全局代理和pac模式同时启动:", "警告", MB_OK);
				RegDeleteValueA(hOpen, "AutoConfigURL");
				AppendMenu(hMenu, MF_STRING, ID_PAC_ENABLE, TEXT("启用PAC代理"));
			}
			else {
				AppendMenu(hMenu, MF_STRING, ID_PAC_ENABLE, TEXT("✔启用PAC代理"));
			}
		}
		else {
			AppendMenu(hMenu, MF_STRING, ID_PAC_ENABLE, TEXT("启用PAC代理"));
		}
		RegCloseKey(hOpen);
	}
}

int SetAutoStartRun(bool enable)
{
	HKEY hOpen;
	int nRet = 0;
	nRet = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", NULL, KEY_ALL_ACCESS, &hOpen);
	if (ERROR_SUCCESS != nRet) {
		MessageBoxA(NULL, "打开注册表失败 Run", "Error", MB_OK);
		nRet = GetLastError();
		return nRet;
	}
	if (!enable)
	{
		nRet = RegDeleteValueA(hOpen, "Blade");
		RegCloseKey(hOpen);
		return nRet;
	}
	char path[256] = {};
	GetModuleFileNameA(NULL, path, 256);
	nRet = RegSetValueExA(hOpen, "Blade", 0, REG_SZ, (const BYTE*)path, (DWORD)strlen(path));
	RegCloseKey(hOpen);
	return nRet;
}

int SetProxyInfo(bool ispac, bool enable)
{
	HKEY hOpen;
	int nRet = 0;
	nRet = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", NULL, KEY_ALL_ACCESS, &hOpen);
	if (ERROR_SUCCESS != nRet) {
		MessageBoxA(NULL, "打开注册表失败 Internet Settings", "Error", MB_OK);
		nRet = GetLastError();
		return nRet;
	}
	
	if (!ispac)
	{
		DWORD nb = 0;
		if (enable) {
			nb = 1;
		}
		nRet = RegSetValueExA(hOpen, "ProxyEnable", 0, REG_DWORD, (const BYTE*)&nb, (DWORD)sizeof(DWORD));
		if (nRet != ERROR_SUCCESS) {
			RegCloseKey(hOpen);
			return nRet;
		}

		if (enable) {
			nRet = RegSetValueExA(hOpen, "ProxyServer", 0, REG_SZ, (const BYTE*)listenAddr, (DWORD)strlen(listenAddr));
			// 关闭pac代理
			RegDeleteValueA(hOpen, "AutoConfigURL");
			RemoveMenu(hMenu, 2, MF_BYPOSITION);
			InsertMenu(hMenu, 2, MF_BYPOSITION | MF_STRING, ID_PAC_DISABLE, TEXT("启用PAC代理"));
		}
	}
	else
	{
		if (enable)
		{
			char data[256] = {};
			snprintf(data, 256, "http://%s/pac", pacListenAddr);
			nRet = RegSetValueExA(hOpen, "AutoConfigURL", 0, REG_SZ, (const BYTE*)data, (DWORD)strlen(data));
			// 关闭全局代理
			DWORD nb = 0;
			nRet = RegSetValueExA(hOpen, "ProxyEnable", 0, REG_DWORD, (const BYTE*)&nb, (DWORD)sizeof(DWORD));
			RemoveMenu(hMenu, 1, MF_BYPOSITION);
			InsertMenu(hMenu, 1, MF_BYPOSITION | MF_STRING, ID_DISABLE, TEXT("启用全局代理"));
		}
		else {
			RegDeleteValueA(hOpen, "AutoConfigURL");
		}
	}
	
	RegCloseKey(hOpen);
	return nRet;
}

void InitProxyMenu()
{
	void* GetServerIndex = GetProcAddress(hd, "GetServerIndex");
	void* GetServerIndexList = GetProcAddress(hd, "GetServerIndexList");

	char* sindex = NULL;
	char* sindexlist = NULL;
	(GetString(GetServerIndex))(&sindex);
	(GetString(GetServerIndexList))(&sindexlist);

	RemoveMenu(hMenu, 3, MF_BYPOSITION);
	DestroyMenu(hProxyMenu);

	hProxyMenu = CreatePopupMenu();

	arr = split(sindexlist, "\n");


	for (int i = 0; i < arr.size(); i++)
	{
		if (arr[i] == sindex){
			AppendMenu(hProxyMenu, MF_STRING, 50000 + i, (TEXT("✔") + s2ws(arr[i])).c_str());
		}
		else {
			AppendMenu(hProxyMenu, MF_STRING, 50000 + i, s2ws(arr[i]).c_str());
		}
	}
	
	InsertMenu(hMenu, 3, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hProxyMenu, TEXT("选择代理服务器"));
}


//实例化托盘
void InitTray(HINSTANCE hInstance, HWND hWnd)
{
	nid.cbSize = sizeof(NOTIFYICONDATA);
	nid.hWnd = hWnd;
	nid.uID = IDI_SMALL;
	nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
	nid.uCallbackMessage = WM_TRAY;
	nid.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SMALL));
	lstrcpy(nid.szTip, APP_NAME);

	hMenu = CreatePopupMenu();//生成托盘菜单
	CheckRegKey();
	
	hProxyMenu = CreatePopupMenu();
	AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hProxyMenu, TEXT("选择代理服务器"));
	InitProxyMenu();
	
	AppendMenu(hMenu, MF_STRING, ID_EDITJSON, TEXT("编辑配置文件"));
	AppendMenu(hMenu, MF_STRING, ID_OPENCUR, TEXT("打开当前文件夹"));
	AppendMenu(hMenu, MF_STRING, ID_SHOW, TEXT("关于"));
	AppendMenu(hMenu, MF_STRING, ID_EXIT, TEXT("退出"));


	Shell_NotifyIcon(NIM_ADD, &nid);
}


//演示托盘气泡提醒
void ShowTrayMsg()
{
	lstrcpy(nid.szInfoTitle, APP_NAME);
	lstrcpy(nid.szInfo, TEXT("收到一条消息！"));
	nid.uTimeout = 1000;
	Shell_NotifyIcon(NIM_MODIFY, &nid);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_TRAY:
		switch (lParam)
		{
		case WM_RBUTTONDOWN:
		{
			//获取鼠标坐标
			POINT pt;
			GetCursorPos(&pt);

			//解决在菜单外单击左键菜单不消失的问题
			SetForegroundWindow(hWnd);

			//使菜单某项变灰
			//EnableMenuItem(hMenu, ID_SHOW, MF_GRAYED);	

			//显示并获取选中的菜单
			int cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, NULL, hWnd, NULL);
			switch (cmd)
			{
			case ID_SHOW:
			{
				void* getseraddr = GetProcAddress(hd, "GetServerAddr");
				(GetString(getseraddr))(&remoteAddr);
				char buf[1024] = {};
				snprintf(buf, 1024, "Listen:%s, Remote:%s", listenAddr, remoteAddr);
				MessageBoxA(hWnd, buf, "Blade", MB_OK);
			}
				break;
			case ID_EXIT:
			{		
				PostMessage(hWnd, WM_DESTROY, NULL, NULL);
			}
				break;
			case ID_EDITJSON:
			{
				char cmd[256] = {};
				snprintf(cmd, 256, "%s\\client_config.json", currentDir);
				ShellExecuteA(hWnd, "open", "notepad", cmd, NULL, SW_NORMAL);
				
//				snprintf(cmd, 256, "notepad %s\\client_config.json", currentDir);
// 				STARTUPINFOA si = { sizeof(si) };
// 				PROCESS_INFORMATION pi;
// 				CreateProcessA(NULL, cmd, NULL, NULL,FALSE, 0, NULL, NULL, &si, &pi);
			}
				break;
			case ID_OPENCUR:
				ShellExecuteA(hWnd, "open", currentDir, NULL, NULL, SW_NORMAL);
				break;
			case ID_AUTO:
				if (0 == SetAutoStartRun(false)){
					RemoveMenu(hMenu, 0, MF_BYPOSITION);
					InsertMenu(hMenu, 0, MF_BYPOSITION | MF_STRING, ID_DISAUTO, TEXT("开机运行"));
				}
				break;
			case ID_DISAUTO:
				if (0 == SetAutoStartRun(true)) {
					RemoveMenu(hMenu, 0, MF_BYPOSITION);
					InsertMenu(hMenu, 0, MF_BYPOSITION | MF_STRING, ID_AUTO, TEXT("✔开机运行"));
				}
				break;
			case ID_ENABLE:
				if (0 == SetProxyInfo(false, false)) {
					RemoveMenu(hMenu, 1, MF_BYPOSITION);
					InsertMenu(hMenu, 1, MF_BYPOSITION | MF_STRING, ID_DISABLE, TEXT("启用全局代理"));
				}
				break;
			case ID_DISABLE:
				if (0 == SetProxyInfo(false, true)) {
					RemoveMenu(hMenu, 1, MF_BYPOSITION);
					InsertMenu(hMenu, 1, MF_BYPOSITION | MF_STRING, ID_ENABLE, TEXT("✔启用全局代理"));
				}
				break;
			case ID_PAC_ENABLE:
				if (0 == SetProxyInfo(true, false)) {
					RemoveMenu(hMenu, 2, MF_BYPOSITION);
					InsertMenu(hMenu, 2, MF_BYPOSITION | MF_STRING, ID_PAC_DISABLE, TEXT("启用PAC代理"));
				}
				break;
			case ID_PAC_DISABLE:
				if (0 == SetProxyInfo(true, true)) {
					RemoveMenu(hMenu, 2, MF_BYPOSITION);
					InsertMenu(hMenu, 2, MF_BYPOSITION | MF_STRING, ID_PAC_ENABLE, TEXT("✔启用PAC代理"));
				}
				break;
			default:
				cmd -= 50000;
				if (cmd >= 0 && cmd < arr.size())
				{
					void* ChangeServerIndex = GetProcAddress(hd, "ChangeServerIndex");

					typedef void(*fun)(const char*);
					(fun(ChangeServerIndex))(arr[cmd].c_str());

					// 重新初始化菜单
					InitProxyMenu();
				}
				break;
			}

		}
			break;
		case WM_LBUTTONDOWN:	
			break;
		case WM_LBUTTONDBLCLK:
			break;
		}
		break;
	case WM_DESTROY:
		//窗口销毁时删除托盘
		Shell_NotifyIcon(NIM_DELETE, &nid);
		PostQuitMessage(0);
		break;
	case WM_TIMER:
		ShowTrayMsg();
		KillTimer(hWnd, wParam);
		break;
	}
	if (uMsg == WM_TASKBAR_CREATED)
	{
		//系统Explorer崩溃重启时，重新加载托盘
		Shell_NotifyIcon(NIM_ADD, &nid);
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}


void WINAPI runClientCore()
{
	char dllpath[256] = {};
	snprintf(dllpath, 256, "%s\\client_cgo.dll", currentDir);
	hd = LoadLibraryA(dllpath);
	if (hd == NULL) {
		return;
	}
	char jsonpath[256] = {};
	snprintf(jsonpath, 256, "%s\\client_config.json", currentDir);

	void* run = GetProcAddress(hd, "RunCore");

	typedef void(*runcore)(const char*, const char*);
	(runcore(run))(currentDir, jsonpath);
}

int InitProxyClient()
{
	DWORD  threadId;
	CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(runClientCore), NULL, 0, &threadId);

	Sleep(2000);

	void* getlisten = GetProcAddress(hd, "GetProxyListen");
	void* getpaclisten = GetProcAddress(hd, "GetPACListen");
	(GetString(getlisten))(&listenAddr);
	(GetString(getpaclisten))(&pacListenAddr);
	
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int iCmdShow)
{
	GetModuleFileNameA(NULL, currentDir, (DWORD)sizeof(currentDir));
	char* pos = strrchr(currentDir, '\\');
	*pos = 0;

	if (0 != InitProxyClient()) {
		MessageBoxA(NULL, "InitProxyClient Error", "Error", MB_OK);
		return -1;
	}

	HWND hWnd;
	MSG msg;
	WNDCLASS wc = { 0 };
	wc.style = NULL;
	wc.hIcon = NULL;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WndProc;
	wc.hbrBackground = NULL;
	wc.lpszMenuName = NULL;
	wc.lpszClassName = APP_NAME;
	wc.hCursor = NULL;

	if (!RegisterClass(&wc)) return 0;

	hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, APP_NAME, APP_NAME, WS_POPUP, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	ShowWindow(hWnd, iCmdShow);
	UpdateWindow(hWnd);

	InitTray(hInstance, hWnd);			//实例化托盘
	//SetTimer(hWnd, 3, 1000, NULL);		//定时发消息，演示气泡提示功能

	
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return 0;
}
